package game.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import common.config.exception.CustomException;
import common.mapper.CommonGBuildingMapper;
import common.model.po.*;
import common.model.request.base.PaginationRequest;
import common.model.response.base.Result;
import common.service.CommonGBuildingLandformService;
import common.service.CommonGRecipeBuildingService;
import common.service.CommonGRecipeItemService;
import game.mapper.GBuildingMapper;
import game.model.converter.GBuildingConverter;
import game.model.request.SaveBuildingRequest;
import game.model.request.SearchBuildingRequest;
import game.model.vo.BuildingDetailVo;
import game.model.vo.SearchBuildingVo;
import game.service.GBuildingService;
import game.service.GClassificationService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 建筑（包括生产机器、物流、照明、墙壁等）服务实现类
 *
 * @author 米泽鹏
 * @since 2022-02-26
 */
@Service
public class GBuildingServiceImpl extends ServiceImpl<CommonGBuildingMapper, GBuilding> implements GBuildingService {

	@Resource
	private GBuildingMapper mapper;
	@Resource
	private GClassificationService classificationService;
	@Resource
	private CommonGBuildingLandformService buildingLandformService;
	@Resource
	private CommonGRecipeBuildingService recipeBuildingService;
	@Resource
	private CommonGRecipeItemService recipeItemService;

	@Override
	public Page<SearchBuildingVo> searchBuildingByPage(PaginationRequest<SearchBuildingRequest, GBuilding> paginationRequest) {
		return mapper.pageSearchBuilding(paginationRequest.buildPage(), paginationRequest.getParams());
	}

	@Override
	public BuildingDetailVo getBuildingDetailById(Long buildingId) {
		GBuilding building = getById(buildingId);
		BuildingDetailVo buildingDetailVo = GBuildingConverter.poToBuildingDetailVo(building);
		// 全类别
		GClassification classification;
		Long classificationId = building.getClassificationId();
		do {
			classification = classificationService.getById(classificationId);
			if (classification != null) {
				buildingDetailVo.getClassificationNameList().add(classification.getName());
				classificationId = classification.getParentId();
			}
		} while (classification != null && classificationId != 0);
		Collections.reverse(buildingDetailVo.getClassificationNameList());
		// 地貌
		List<GBuildingLandform> buildingLandformList = buildingLandformService.list(new LambdaQueryWrapper<GBuildingLandform>().eq(GBuildingLandform::getBuildingId, buildingId).select(GBuildingLandform::getLandformName));
		List<String> landformNameList = buildingLandformList.stream().map(GBuildingLandform::getLandformName).collect(Collectors.toList());
		buildingDetailVo.getLandformNameList().addAll(landformNameList);
		return buildingDetailVo;
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public Result<?> saveBuilding(SaveBuildingRequest request) {
		GBuilding gBuilding = GBuildingConverter.requestToPo(request);
		if (gBuilding.getId() == null) {
			save(gBuilding);
		} else {
			// 建筑和地貌的关联关系，先删后增
			buildingLandformService.remove(new LambdaQueryWrapper<GBuildingLandform>().eq(GBuildingLandform::getBuildingId, request.getId()));
			if (!request.getLandformList().isEmpty()) {
				List<GBuildingLandform> buildingLandformList = getBuildingLandformList(request, gBuilding);
				buildingLandformService.saveBatch(buildingLandformList);
			}
			// 建筑名称修改后需要连带修改g_recipe_item表的item_name字段、g_recipe_building表的building_name字段、g_building_landform表的building_name字段（已在上一步中修改）
			GBuilding gBuildingOriginal = getById(gBuilding.getId());
			if (!gBuildingOriginal.getBuildingName().equals(gBuilding.getBuildingName())) {
				recipeItemService.lambdaUpdate().eq(GRecipeItem::getType, 1).eq(GRecipeItem::getProductType, 2).eq(GRecipeItem::getItemId, gBuilding.getId()).set(GRecipeItem::getItemName, gBuilding.getBuildingName()).update();
				recipeBuildingService.lambdaUpdate().eq(GRecipeBuilding::getBuildingId, gBuilding.getId()).set(GRecipeBuilding::getBuildingName, gBuilding.getBuildingName()).update();
			}
			// 建筑
			updateById(gBuilding);
		}
		// 建筑和地貌的关联关系
		if (request.getId() != null) {
			buildingLandformService.remove(new LambdaQueryWrapper<GBuildingLandform>().eq(GBuildingLandform::getBuildingId, request.getId()));
		}
		if (!request.getLandformList().isEmpty()) {
			List<GBuildingLandform> buildingLandformList = getBuildingLandformList(request, gBuilding);
			buildingLandformService.saveBatch(buildingLandformList);
		}
		return Result.success();
	}

	private List<GBuildingLandform> getBuildingLandformList(SaveBuildingRequest request, GBuilding gBuilding) {
		List<GBuildingLandform> buildingLandformList = Lists.newArrayList();
		for (GLandform landform : request.getLandformList()) {
			GBuildingLandform gBuildingLandform = new GBuildingLandform();
			gBuildingLandform.setBuildingId(gBuilding.getId());
			gBuildingLandform.setBuildingName(gBuilding.getBuildingName());
			gBuildingLandform.setLandformId(landform.getId());
			gBuildingLandform.setLandformName(landform.getLandformName());
			buildingLandformList.add(gBuildingLandform);
		}
		return buildingLandformList;
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public Result<?> deleteBuilding(Long id) {
		// 检查是否有配方占用此建筑
		List<GRecipeBuilding> recipeBuildingList = recipeBuildingService.lambdaQuery().eq(GRecipeBuilding::getBuildingId, id).list();
		Set<Long> recipeIdSet = recipeBuildingList.stream().map(GRecipeBuilding::getRecipeId).collect(Collectors.toSet());
		if (!recipeIdSet.isEmpty()) {
			throw new CustomException("有" + recipeIdSet.size() + "个配方使用此建筑，无法删除");
		}
		// 删除建筑
		removeById(id);
		// 删除建筑和地貌的关联关系
		buildingLandformService.remove(new LambdaQueryWrapper<GBuildingLandform>().eq(GBuildingLandform::getBuildingId, id));
		return Result.success();
	}

}
